/*! Responsive 2.2.5
 * 2014-2020 SpryMedia Ltd - datatables.net/license
 */

/**
 * @summary     Responsive
 * @description Responsive tables plug-in for DataTables
 * @version     2.2.5
 * @file        dataTables.responsive.js
 * @author      SpryMedia Ltd (www.sprymedia.co.uk)
 * @contact     www.sprymedia.co.uk/contact
 * @copyright   Copyright 2014-2020 SpryMedia Ltd.
 *
 * This source file is free software, available under the following license:
 *   MIT license - http://datatables.net/license/mit
 *
 * This source file is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
 *
 * For details please refer to: http://www.datatables.net
 */
(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD
        define(['jquery', 'datatables.net'], function ($) {
            return factory($, window, document);
        });
    } else if (typeof exports === 'object') {
        // CommonJS
        module.exports = function (root, $) {
            if (!root) {
                root = window;
            }

            if (!$ || !$.fn.dataTable) {
                $ = require('datatables.net')(root, $).$;
            }

            return factory($, root, root.document);
        };
    } else {
        // Browser
        factory(jQuery, window, document);
    }
}(function ($, window, document, undefined) {
    'use strict';
    var DataTable = $.fn.dataTable;


    /**
     * Responsive is a plug-in for the DataTables library that makes use of
     * DataTables' ability to change the visibility of columns, changing the
     * visibility of columns so the displayed columns fit into the table container.
     * The end result is that complex tables will be dynamically adjusted to fit
     * into the viewport, be it on a desktop, tablet or mobile browser.
     *
     * Responsive for DataTables has two modes of operation, which can used
     * individually or combined:
     *
     * * Class name based control - columns assigned class names that match the
     *   breakpoint logic can be shown / hidden as required for each breakpoint.
     * * Automatic control - columns are automatically hidden when there is no
     *   room left to display them. Columns removed from the right.
     *
     * In additional to column visibility control, Responsive also has built into
     * options to use DataTables' child row display to show / hide the information
     * from the table that has been hidden. There are also two modes of operation
     * for this child row display:
     *
     * * Inline - when the control element that the user can use to show / hide
     *   child rows is displayed inside the first column of the table.
     * * Column - where a whole column is dedicated to be the show / hide control.
     *
     * Initialisation of Responsive is performed by:
     *
     * * Adding the class `responsive` or `dt-responsive` to the table. In this case
     *   Responsive will automatically be initialised with the default configuration
     *   options when the DataTable is created.
     * * Using the `responsive` option in the DataTables configuration options. This
     *   can also be used to specify the configuration options, or simply set to
     *   `true` to use the defaults.
     *
     *  @class
     *  @param {object} settings DataTables settings object for the host table
     *  @param {object} [opts] Configuration options
     *  @requires jQuery 1.7+
     *  @requires DataTables 1.10.3+
     *
     *  @example
     *      $('#example').DataTable( {
     *        responsive: true
     *      } );
     *    } );
     */
    var Responsive = function (settings, opts) {
        // Sanity check that we are using DataTables 1.10 or newer
        if (!DataTable.versionCheck || !DataTable.versionCheck('1.10.10')) {
            throw 'DataTables Responsive requires DataTables 1.10.10 or newer';
        }

        this.s = {
            dt: new DataTable.Api(settings),
            columns: [],
            current: []
        };

        // Check if responsive has already been initialised on this table
        if (this.s.dt.settings()[0].responsive) {
            return;
        }

        // details is an object, but for simplicity the user can give it as a string
        // or a boolean
        if (opts && typeof opts.details === 'string') {
            opts.details = {type: opts.details};
        } else if (opts && opts.details === false) {
            opts.details = {type: false};
        } else if (opts && opts.details === true) {
            opts.details = {type: 'inline'};
        }

        this.c = $.extend(true, {}, Responsive.defaults, DataTable.defaults.responsive, opts);
        settings.responsive = this;
        this._constructor();
    };

    $.extend(Responsive.prototype, {
        /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
         * Constructor
         */

        /**
         * Initialise the Responsive instance
         *
         * @private
         */
        _constructor: function () {
            var that = this;
            var dt = this.s.dt;
            var dtPrivateSettings = dt.settings()[0];
            var oldWindowWidth = $(window).innerWidth();

            dt.settings()[0]._responsive = this;

            // Use DataTables' throttle function to avoid processor thrashing on
            // resize
            $(window).on('resize.dtr orientationchange.dtr', DataTable.util.throttle(function () {
                // iOS has a bug whereby resize can fire when only scrolling
                // See: http://stackoverflow.com/questions/8898412
                var width = $(window).innerWidth();

                if (width !== oldWindowWidth) {
                    that._resize();
                    oldWindowWidth = width;
                }
            }));

            // DataTables doesn't currently trigger an event when a row is added, so
            // we need to hook into its private API to enforce the hidden rows when
            // new data is added
            dtPrivateSettings.oApi._fnCallbackReg(dtPrivateSettings, 'aoRowCreatedCallback', function (tr, data, idx) {
                if ($.inArray(false, that.s.current) !== -1) {
                    $('>td, >th', tr).each(function (i) {
                        var idx = dt.column.index('toData', i);

                        if (that.s.current[idx] === false) {
                            $(this).css('display', 'none');
                        }
                    });
                }
            });

            // Destroy event handler
            dt.on('destroy.dtr', function () {
                dt.off('.dtr');
                $(dt.table().body()).off('.dtr');
                $(window).off('resize.dtr orientationchange.dtr');
                dt.cells('.dtr-control').nodes().to$().removeClass('dtr-control');

                // Restore the columns that we've hidden
                $.each(that.s.current, function (i, val) {
                    if (val === false) {
                        that._setColumnVis(i, true);
                    }
                });
            });

            // Reorder the breakpoints array here in case they have been added out
            // of order
            this.c.breakpoints.sort(function (a, b) {
                return a.width < b.width ? 1 :
                    a.width > b.width ? -1 : 0;
            });

            this._classLogic();
            this._resizeAuto();

            // Details handler
            var details = this.c.details;

            if (details.type !== false) {
                that._detailsInit();

                // DataTables will trigger this event on every column it shows and
                // hides individually
                dt.on('column-visibility.dtr', function () {
                    // Use a small debounce to allow multiple columns to be set together
                    if (that._timer) {
                        clearTimeout(that._timer);
                    }

                    that._timer = setTimeout(function () {
                        that._timer = null;

                        that._classLogic();
                        that._resizeAuto();
                        that._resize();

                        that._redrawChildren();
                    }, 100);
                });

                // Redraw the details box on each draw which will happen if the data
                // has changed. This is used until DataTables implements a native
                // `updated` event for rows
                dt.on('draw.dtr', function () {
                    that._redrawChildren();
                });

                $(dt.table().node()).addClass('dtr-' + details.type);
            }

            dt.on('column-reorder.dtr', function (e, settings, details) {
                that._classLogic();
                that._resizeAuto();
                that._resize(true);
            });

            // Change in column sizes means we need to calc
            dt.on('column-sizing.dtr', function () {
                that._resizeAuto();
                that._resize();
            });

            // On Ajax reload we want to reopen any child rows which are displayed
            // by responsive
            dt.on('preXhr.dtr', function () {
                var rowIds = [];
                dt.rows().every(function () {
                    if (this.child.isShown()) {
                        rowIds.push(this.id(true));
                    }
                });

                dt.one('draw.dtr', function () {
                    that._resizeAuto();
                    that._resize();

                    dt.rows(rowIds).every(function () {
                        that._detailsDisplay(this, false);
                    });
                });
            });

            dt
                .on('draw.dtr', function () {
                    that._controlClass();
                })
                .on('init.dtr', function (e, settings, details) {
                    if (e.namespace !== 'dt') {
                        return;
                    }

                    that._resizeAuto();
                    that._resize();

                    // If columns were hidden, then DataTables needs to adjust the
                    // column sizing
                    if ($.inArray(false, that.s.current)) {
                        dt.columns.adjust();
                    }
                });

            // First pass - draw the table for the current viewport size
            this._resize();
        },


        /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
         * Private methods
         */

        /**
         * Calculate the visibility for the columns in a table for a given
         * breakpoint. The result is pre-determined based on the class logic if
         * class names are used to control all columns, but the width of the table
         * is also used if there are columns which are to be automatically shown
         * and hidden.
         *
         * @param  {string} breakpoint Breakpoint name to use for the calculation
         * @return {array} Array of boolean values initiating the visibility of each
         *   column.
         *  @private
         */
        _columnsVisiblity: function (breakpoint) {
            var dt = this.s.dt;
            var columns = this.s.columns;
            var i, ien;

            // Create an array that defines the column ordering based first on the
            // column's priority, and secondly the column index. This allows the
            // columns to be removed from the right if the priority matches
            var order = columns
                .map(function (col, idx) {
                    return {
                        columnIdx: idx,
                        priority: col.priority
                    };
                })
                .sort(function (a, b) {
                    if (a.priority !== b.priority) {
                        return a.priority - b.priority;
                    }
                    return a.columnIdx - b.columnIdx;
                });

            // Class logic - determine which columns are in this breakpoint based
            // on the classes. If no class control (i.e. `auto`) then `-` is used
            // to indicate this to the rest of the function
            var display = $.map(columns, function (col, i) {
                if (dt.column(i).visible() === false) {
                    return 'not-visible';
                }
                return col.auto && col.minWidth === null ?
                    false :
                    col.auto === true ?
                        '-' :
                        $.inArray(breakpoint, col.includeIn) !== -1;
            });

            // Auto column control - first pass: how much width is taken by the
            // ones that must be included from the non-auto columns
            var requiredWidth = 0;
            for (i = 0, ien = display.length; i < ien; i++) {
                if (display[i] === true) {
                    requiredWidth += columns[i].minWidth;
                }
            }

            // Second pass, use up any remaining width for other columns. For
            // scrolling tables we need to subtract the width of the scrollbar. It
            // may not be requires which makes this sub-optimal, but it would
            // require another full redraw to make complete use of those extra few
            // pixels
            var scrolling = dt.settings()[0].oScroll;
            var bar = scrolling.sY || scrolling.sX ? scrolling.iBarWidth : 0;
            var widthAvailable = dt.table().container().offsetWidth - bar;
            var usedWidth = widthAvailable - requiredWidth;

            // Control column needs to always be included. This makes it sub-
            // optimal in terms of using the available with, but to stop layout
            // thrashing or overflow. Also we need to account for the control column
            // width first so we know how much width is available for the other
            // columns, since the control column might not be the first one shown
            for (i = 0, ien = display.length; i < ien; i++) {
                if (columns[i].control) {
                    usedWidth -= columns[i].minWidth;
                }
            }

            // Allow columns to be shown (counting by priority and then right to
            // left) until we run out of room
            var empty = false;
            for (i = 0, ien = order.length; i < ien; i++) {
                var colIdx = order[i].columnIdx;

                if (display[colIdx] === '-' && !columns[colIdx].control && columns[colIdx].minWidth) {
                    // Once we've found a column that won't fit we don't let any
                    // others display either, or columns might disappear in the
                    // middle of the table
                    if (empty || usedWidth - columns[colIdx].minWidth < 0) {
                        empty = true;
                        display[colIdx] = false;
                    } else {
                        display[colIdx] = true;
                    }

                    usedWidth -= columns[colIdx].minWidth;
                }
            }

            // Determine if the 'control' column should be shown (if there is one).
            // This is the case when there is a hidden column (that is not the
            // control column). The two loops look inefficient here, but they are
            // trivial and will fly through. We need to know the outcome from the
            // first , before the action in the second can be taken
            var showControl = false;

            for (i = 0, ien = columns.length; i < ien; i++) {
                if (!columns[i].control && !columns[i].never && display[i] === false) {
                    showControl = true;
                    break;
                }
            }

            for (i = 0, ien = columns.length; i < ien; i++) {
                if (columns[i].control) {
                    display[i] = showControl;
                }

                // Replace not visible string with false from the control column detection above
                if (display[i] === 'not-visible') {
                    display[i] = false;
                }
            }

            // Finally we need to make sure that there is at least one column that
            // is visible
            if ($.inArray(true, display) === -1) {
                display[0] = true;
            }

            return display;
        },


        /**
         * Create the internal `columns` array with information about the columns
         * for the table. This includes determining which breakpoints the column
         * will appear in, based upon class names in the column, which makes up the
         * vast majority of this method.
         *
         * @private
         */
        _classLogic: function () {
            var that = this;
            var calc = {};
            var breakpoints = this.c.breakpoints;
            var dt = this.s.dt;
            var columns = dt.columns().eq(0).map(function (i) {
                var column = this.column(i);
                var className = column.header().className;
                var priority = dt.settings()[0].aoColumns[i].responsivePriority;
                var dataPriority = column.header().getAttribute('data-priority');

                if (priority === undefined) {
                    priority = dataPriority === undefined || dataPriority === null ?
                        10000 :
                        dataPriority * 1;
                }

                return {
                    className: className,
                    includeIn: [],
                    auto: false,
                    control: false,
                    never: className.match(/\bnever\b/) ? true : false,
                    priority: priority
                };
            });

            // Simply add a breakpoint to `includeIn` array, ensuring that there are
            // no duplicates
            var add = function (colIdx, name) {
                var includeIn = columns[colIdx].includeIn;

                if ($.inArray(name, includeIn) === -1) {
                    includeIn.push(name);
                }
            };

            var column = function (colIdx, name, operator, matched) {
                var size, i, ien;

                if (!operator) {
                    columns[colIdx].includeIn.push(name);
                } else if (operator === 'max-') {
                    // Add this breakpoint and all smaller
                    size = that._find(name).width;

                    for (i = 0, ien = breakpoints.length; i < ien; i++) {
                        if (breakpoints[i].width <= size) {
                            add(colIdx, breakpoints[i].name);
                        }
                    }
                } else if (operator === 'min-') {
                    // Add this breakpoint and all larger
                    size = that._find(name).width;

                    for (i = 0, ien = breakpoints.length; i < ien; i++) {
                        if (breakpoints[i].width >= size) {
                            add(colIdx, breakpoints[i].name);
                        }
                    }
                } else if (operator === 'not-') {
                    // Add all but this breakpoint
                    for (i = 0, ien = breakpoints.length; i < ien; i++) {
                        if (breakpoints[i].name.indexOf(matched) === -1) {
                            add(colIdx, breakpoints[i].name);
                        }
                    }
                }
            };

            // Loop over each column and determine if it has a responsive control
            // class
            columns.each(function (col, i) {
                var classNames = col.className.split(' ');
                var hasClass = false;

                // Split the class name up so multiple rules can be applied if needed
                for (var k = 0, ken = classNames.length; k < ken; k++) {
                    var className = $.trim(classNames[k]);

                    if (className === 'all') {
                        // Include in all
                        hasClass = true;
                        col.includeIn = $.map(breakpoints, function (a) {
                            return a.name;
                        });
                        return;
                    } else if (className === 'none' || col.never) {
                        // Include in none (default) and no auto
                        hasClass = true;
                        return;
                    } else if (className === 'control') {
                        // Special column that is only visible, when one of the other
                        // columns is hidden. This is used for the details control
                        hasClass = true;
                        col.control = true;
                        return;
                    }

                    $.each(breakpoints, function (j, breakpoint) {
                        // Does this column have a class that matches this breakpoint?
                        var brokenPoint = breakpoint.name.split('-');
                        var re = new RegExp('(min\\-|max\\-|not\\-)?(' + brokenPoint[0] + ')(\\-[_a-zA-Z0-9])?');
                        var match = className.match(re);

                        if (match) {
                            hasClass = true;

                            if (match[2] === brokenPoint[0] && match[3] === '-' + brokenPoint[1]) {
                                // Class name matches breakpoint name fully
                                column(i, breakpoint.name, match[1], match[2] + match[3]);
                            } else if (match[2] === brokenPoint[0] && !match[3]) {
                                // Class name matched primary breakpoint name with no qualifier
                                column(i, breakpoint.name, match[1], match[2]);
                            }
                        }
                    });
                }

                // If there was no control class, then automatic sizing is used
                if (!hasClass) {
                    col.auto = true;
                }
            });

            this.s.columns = columns;
        },

        /**
         * Update the cells to show the correct control class / button
         * @private
         */
        _controlClass: function () {
            if (this.c.details.type === 'inline') {
                var dt = this.s.dt;
                var columnsVis = this.s.current;
                var firstVisible = $.inArray(true, columnsVis);

                // Remove from any cells which shouldn't have it
                dt.cells(
                    null,
                    function (idx) {
                        return idx !== firstVisible;
                    },
                    {page: 'current'}
                )
                    .nodes()
                    .to$()
                    .filter('.dtr-control')
                    .removeClass('dtr-control');

                dt.cells(null, firstVisible, {page: 'current'})
                    .nodes()
                    .to$()
                    .addClass('dtr-control');
            }
        },

        /**
         * Show the details for the child row
         *
         * @param  {DataTables.Api} row    API instance for the row
         * @param  {boolean}        update Update flag
         * @private
         */
        _detailsDisplay: function (row, update) {
            var that = this;
            var dt = this.s.dt;
            var details = this.c.details;

            if (details && details.type !== false) {
                var res = details.display(row, update, function () {
                    return details.renderer(
                        dt, row[0], that._detailsObj(row[0])
                    );
                });

                if (res === true || res === false) {
                    $(dt.table().node()).triggerHandler('responsive-display.dt', [dt, row, res, update]);
                }
            }
        },


        /**
         * Initialisation for the details handler
         *
         * @private
         */
        _detailsInit: function () {
            var that = this;
            var dt = this.s.dt;
            var details = this.c.details;

            // The inline type always uses the first child as the target
            if (details.type === 'inline') {
                details.target = 'td.dtr-control, th.dtr-control';
            }

            // Keyboard accessibility
            dt.on('draw.dtr', function () {
                that._tabIndexes();
            });
            that._tabIndexes(); // Initial draw has already happened

            $(dt.table().body()).on('keyup.dtr', 'td, th', function (e) {
                if (e.keyCode === 13 && $(this).data('dtr-keyboard')) {
                    $(this).click();
                }
            });

            // type.target can be a string jQuery selector or a column index
            var target = details.target;
            var selector = typeof target === 'string' ? target : 'td, th';

            if (target !== undefined || target !== null) {
                // Click handler to show / hide the details rows when they are available
                $(dt.table().body())
                    .on('click.dtr mousedown.dtr mouseup.dtr', selector, function (e) {
                        // If the table is not collapsed (i.e. there is no hidden columns)
                        // then take no action
                        if (!$(dt.table().node()).hasClass('collapsed')) {
                            return;
                        }

                        // Check that the row is actually a DataTable's controlled node
                        if ($.inArray($(this).closest('tr').get(0), dt.rows().nodes().toArray()) === -1) {
                            return;
                        }

                        // For column index, we determine if we should act or not in the
                        // handler - otherwise it is already okay
                        if (typeof target === 'number') {
                            var targetIdx = target < 0 ?
                                dt.columns().eq(0).length + target :
                                target;

                            if (dt.cell(this).index().column !== targetIdx) {
                                return;
                            }
                        }

                        // $().closest() includes itself in its check
                        var row = dt.row($(this).closest('tr'));

                        // Check event type to do an action
                        if (e.type === 'click') {
                            // The renderer is given as a function so the caller can execute it
                            // only when they need (i.e. if hiding there is no point is running
                            // the renderer)
                            that._detailsDisplay(row, false);
                        } else if (e.type === 'mousedown') {
                            // For mouse users, prevent the focus ring from showing
                            $(this).css('outline', 'none');
                        } else if (e.type === 'mouseup') {
                            // And then re-allow at the end of the click
                            $(this).trigger('blur').css('outline', '');
                        }
                    });
            }
        },


        /**
         * Get the details to pass to a renderer for a row
         * @param  {int} rowIdx Row index
         * @private
         */
        _detailsObj: function (rowIdx) {
            var that = this;
            var dt = this.s.dt;

            return $.map(this.s.columns, function (col, i) {
                // Never and control columns should not be passed to the renderer
                if (col.never || col.control) {
                    return;
                }

                var dtCol = dt.settings()[0].aoColumns[i];

                return {
                    className: dtCol.sClass,
                    columnIndex: i,
                    data: dt.cell(rowIdx, i).render(that.c.orthogonal),
                    hidden: dt.column(i).visible() && !that.s.current[i],
                    rowIndex: rowIdx,
                    title: dtCol.sTitle !== null ?
                        dtCol.sTitle :
                        $(dt.column(i).header()).text()
                };
            });
        },


        /**
         * Find a breakpoint object from a name
         *
         * @param  {string} name Breakpoint name to find
         * @return {object}      Breakpoint description object
         * @private
         */
        _find: function (name) {
            var breakpoints = this.c.breakpoints;

            for (var i = 0, ien = breakpoints.length; i < ien; i++) {
                if (breakpoints[i].name === name) {
                    return breakpoints[i];
                }
            }
        },


        /**
         * Re-create the contents of the child rows as the display has changed in
         * some way.
         *
         * @private
         */
        _redrawChildren: function () {
            var that = this;
            var dt = this.s.dt;

            dt.rows({page: 'current'}).iterator('row', function (settings, idx) {
                var row = dt.row(idx);

                that._detailsDisplay(dt.row(idx), true);
            });
        },


        /**
         * Alter the table display for a resized viewport. This involves first
         * determining what breakpoint the window currently is in, getting the
         * column visibilities to apply and then setting them.
         *
         * @param  {boolean} forceRedraw Force a redraw
         * @private
         */
        _resize: function (forceRedraw) {
            var that = this;
            var dt = this.s.dt;
            var width = $(window).innerWidth();
            var breakpoints = this.c.breakpoints;
            var breakpoint = breakpoints[0].name;
            var columns = this.s.columns;
            var i, ien;
            var oldVis = this.s.current.slice();

            // Determine what breakpoint we are currently at
            for (i = breakpoints.length - 1; i >= 0; i--) {
                if (width <= breakpoints[i].width) {
                    breakpoint = breakpoints[i].name;
                    break;
                }
            }

            // Show the columns for that break point
            var columnsVis = this._columnsVisiblity(breakpoint);
            this.s.current = columnsVis;

            // Set the class before the column visibility is changed so event
            // listeners know what the state is. Need to determine if there are
            // any columns that are not visible but can be shown
            var collapsedClass = false;

            for (i = 0, ien = columns.length; i < ien; i++) {
                if (columnsVis[i] === false && !columns[i].never && !columns[i].control && !dt.column(i).visible() === false) {
                    collapsedClass = true;
                    break;
                }
            }

            $(dt.table().node()).toggleClass('collapsed', collapsedClass);

            var changed = false;
            var visible = 0;

            dt.columns().eq(0).each(function (colIdx, i) {
                if (columnsVis[i] === true) {
                    visible++;
                }

                if (forceRedraw || columnsVis[i] !== oldVis[i]) {
                    changed = true;
                    that._setColumnVis(colIdx, columnsVis[i]);
                }
            });

            if (changed) {
                this._redrawChildren();

                // Inform listeners of the change
                $(dt.table().node()).trigger('responsive-resize.dt', [dt, this.s.current]);

                // If no records, update the "No records" display element
                if (dt.page.info().recordsDisplay === 0) {
                    $('td', dt.table().body()).eq(0).attr('colspan', visible);
                }
            }
        },


        /**
         * Determine the width of each column in the table so the auto column hiding
         * has that information to work with. This method is never going to be 100%
         * perfect since column widths can change slightly per page, but without
         * seriously compromising performance this is quite effective.
         *
         * @private
         */
        _resizeAuto: function () {
            var dt = this.s.dt;
            var columns = this.s.columns;

            // Are we allowed to do auto sizing?
            if (!this.c.auto) {
                return;
            }

            // Are there any columns that actually need auto-sizing, or do they all
            // have classes defined
            if ($.inArray(true, $.map(columns, function (c) {
                return c.auto;
            })) === -1) {
                return;
            }

            // Need to restore all children. They will be reinstated by a re-render
            if (!$.isEmptyObject(_childNodeStore)) {
                $.each(_childNodeStore, function (key) {
                    var idx = key.split('-');

                    _childNodesRestore(dt, idx[0] * 1, idx[1] * 1);
                });
            }

            // Clone the table with the current data in it
            var tableWidth = dt.table().node().offsetWidth;
            var columnWidths = dt.columns;
            var clonedTable = dt.table().node().cloneNode(false);
            var clonedHeader = $(dt.table().header().cloneNode(false)).appendTo(clonedTable);
            var clonedBody = $(dt.table().body()).clone(false, false).empty().appendTo(clonedTable); // use jQuery because of IE8

            clonedTable.style.width = 'auto';

            // Header
            var headerCells = dt.columns()
                .header()
                .filter(function (idx) {
                    return dt.column(idx).visible();
                })
                .to$()
                .clone(false)
                .css('display', 'table-cell')
                .css('width', 'auto')
                .css('min-width', 0);

            // Body rows - we don't need to take account of DataTables' column
            // visibility since we implement our own here (hence the `display` set)
            $(clonedBody)
                .append($(dt.rows({page: 'current'}).nodes()).clone(false))
                .find('th, td').css('display', '');

            // Footer
            var footer = dt.table().footer();
            if (footer) {
                var clonedFooter = $(footer.cloneNode(false)).appendTo(clonedTable);
                var footerCells = dt.columns()
                    .footer()
                    .filter(function (idx) {
                        return dt.column(idx).visible();
                    })
                    .to$()
                    .clone(false)
                    .css('display', 'table-cell');

                $('<tr/>')
                    .append(footerCells)
                    .appendTo(clonedFooter);
            }

            $('<tr/>')
                .append(headerCells)
                .appendTo(clonedHeader);

            // In the inline case extra padding is applied to the first column to
            // give space for the show / hide icon. We need to use this in the
            // calculation
            if (this.c.details.type === 'inline') {
                $(clonedTable).addClass('dtr-inline collapsed');
            }

            // It is unsafe to insert elements with the same name into the DOM
            // multiple times. For example, cloning and inserting a checked radio
            // clears the chcecked state of the original radio.
            $(clonedTable).find('[name]').removeAttr('name');

            // A position absolute table would take the table out of the flow of
            // our container element, bypassing the height and width (Scroller)
            $(clonedTable).css('position', 'relative')

            var inserted = $('<div/>')
                .css({
                    width: 1,
                    height: 1,
                    overflow: 'hidden',
                    clear: 'both'
                })
                .append(clonedTable);

            inserted.insertBefore(dt.table().node());

            // The cloned header now contains the smallest that each column can be
            headerCells.each(function (i) {
                var idx = dt.column.index('fromVisible', i);
                columns[idx].minWidth = this.offsetWidth || 0;
            });

            inserted.remove();
        },

        /**
         * Get the state of the current hidden columns - controlled by Responsive only
         */
        _responsiveOnlyHidden: function () {
            var dt = this.s.dt;

            return $.map(this.s.current, function (v, i) {
                // If the column is hidden by DataTables then it can't be hidden by
                // Responsive!
                if (dt.column(i).visible() === false) {
                    return true;
                }
                return v;
            });
        },

        /**
         * Set a column's visibility.
         *
         * We don't use DataTables' column visibility controls in order to ensure
         * that column visibility can Responsive can no-exist. Since only IE8+ is
         * supported (and all evergreen browsers of course) the control of the
         * display attribute works well.
         *
         * @param {integer} col      Column index
         * @param {boolean} showHide Show or hide (true or false)
         * @private
         */
        _setColumnVis: function (col, showHide) {
            var dt = this.s.dt;
            var display = showHide ? '' : 'none'; // empty string will remove the attr

            $(dt.column(col).header()).css('display', display);
            $(dt.column(col).footer()).css('display', display);
            dt.column(col).nodes().to$().css('display', display);

            // If the are child nodes stored, we might need to reinsert them
            if (!$.isEmptyObject(_childNodeStore)) {
                dt.cells(null, col).indexes().each(function (idx) {
                    _childNodesRestore(dt, idx.row, idx.column);
                });
            }
        },


        /**
         * Update the cell tab indexes for keyboard accessibility. This is called on
         * every table draw - that is potentially inefficient, but also the least
         * complex option given that column visibility can change on the fly. Its a
         * shame user-focus was removed from CSS 3 UI, as it would have solved this
         * issue with a single CSS statement.
         *
         * @private
         */
        _tabIndexes: function () {
            var dt = this.s.dt;
            var cells = dt.cells({page: 'current'}).nodes().to$();
            var ctx = dt.settings()[0];
            var target = this.c.details.target;

            cells.filter('[data-dtr-keyboard]').removeData('[data-dtr-keyboard]');

            if (typeof target === 'number') {
                dt.cells(null, target, {page: 'current'}).nodes().to$()
                    .attr('tabIndex', ctx.iTabIndex)
                    .data('dtr-keyboard', 1);
            } else {
                // This is a bit of a hack - we need to limit the selected nodes to just
                // those of this table
                if (target === 'td:first-child, th:first-child') {
                    target = '>td:first-child, >th:first-child';
                }

                $(target, dt.rows({page: 'current'}).nodes())
                    .attr('tabIndex', ctx.iTabIndex)
                    .data('dtr-keyboard', 1);
            }
        }
    });


    /**
     * List of default breakpoints. Each item in the array is an object with two
     * properties:
     *
     * * `name` - the breakpoint name.
     * * `width` - the breakpoint width
     *
     * @name Responsive.breakpoints
     * @static
     */
    Responsive.breakpoints = [
        {name: 'desktop', width: Infinity},
        {name: 'tablet-l', width: 1024},
        {name: 'tablet-p', width: 768},
        {name: 'mobile-l', width: 480},
        {name: 'mobile-p', width: 320}
    ];


    /**
     * Display methods - functions which define how the hidden data should be shown
     * in the table.
     *
     * @namespace
     * @name Responsive.defaults
     * @static
     */
    Responsive.display = {
        childRow: function (row, update, render) {
            if (update) {
                if ($(row.node()).hasClass('parent')) {
                    row.child(render(), 'child').show();

                    return true;
                }
            } else {
                if (!row.child.isShown()) {
                    row.child(render(), 'child').show();
                    $(row.node()).addClass('parent');

                    return true;
                } else {
                    row.child(false);
                    $(row.node()).removeClass('parent');

                    return false;
                }
            }
        },

        childRowImmediate: function (row, update, render) {
            if ((!update && row.child.isShown()) || !row.responsive.hasHidden()) {
                // User interaction and the row is show, or nothing to show
                row.child(false);
                $(row.node()).removeClass('parent');

                return false;
            } else {
                // Display
                row.child(render(), 'child').show();
                $(row.node()).addClass('parent');

                return true;
            }
        },

        // This is a wrapper so the modal options for Bootstrap and jQuery UI can
        // have options passed into them. This specific one doesn't need to be a
        // function but it is for consistency in the `modal` name
        modal: function (options) {
            return function (row, update, render) {
                if (!update) {
                    // Show a modal
                    var close = function () {
                        modal.remove(); // will tidy events for us
                        $(document).off('keypress.dtr');
                    };

                    var modal = $('<div class="dtr-modal"/>')
                        .append($('<div class="dtr-modal-display"/>')
                            .append($('<div class="dtr-modal-content"/>')
                                .append(render())
                            )
                            .append($('<div class="dtr-modal-close">&times;</div>')
                                .click(function () {
                                    close();
                                })
                            )
                        )
                        .append($('<div class="dtr-modal-background"/>')
                            .click(function () {
                                close();
                            })
                        )
                        .appendTo('body');

                    $(document).on('keyup.dtr', function (e) {
                        if (e.keyCode === 27) {
                            e.stopPropagation();

                            close();
                        }
                    });
                } else {
                    $('div.dtr-modal-content')
                        .empty()
                        .append(render());
                }

                if (options && options.header) {
                    $('div.dtr-modal-content').prepend(
                        '<h2>' + options.header(row) + '</h2>'
                    );
                }
            };
        }
    };


    var _childNodeStore = {};

    function _childNodes(dt, row, col) {
        var name = row + '-' + col;

        if (_childNodeStore[name]) {
            return _childNodeStore[name];
        }

        // https://jsperf.com/childnodes-array-slice-vs-loop
        var nodes = [];
        var children = dt.cell(row, col).node().childNodes;
        for (var i = 0, ien = children.length; i < ien; i++) {
            nodes.push(children[i]);
        }

        _childNodeStore[name] = nodes;

        return nodes;
    }

    function _childNodesRestore(dt, row, col) {
        var name = row + '-' + col;

        if (!_childNodeStore[name]) {
            return;
        }

        var node = dt.cell(row, col).node();
        var store = _childNodeStore[name];
        var parent = store[0].parentNode;
        var parentChildren = parent.childNodes;
        var a = [];

        for (var i = 0, ien = parentChildren.length; i < ien; i++) {
            a.push(parentChildren[i]);
        }

        for (var j = 0, jen = a.length; j < jen; j++) {
            node.appendChild(a[j]);
        }

        _childNodeStore[name] = undefined;
    }


    /**
     * Display methods - functions which define how the hidden data should be shown
     * in the table.
     *
     * @namespace
     * @name Responsive.defaults
     * @static
     */
    Responsive.renderer = {
        listHiddenNodes: function () {
            return function (api, rowIdx, columns) {
                var ul = $('<ul data-dtr-index="' + rowIdx + '" class="dtr-details"/>');
                var found = false;

                var data = $.each(columns, function (i, col) {
                    if (col.hidden) {
                        var klass = col.className ?
                            'class="' + col.className + '"' :
                            '';

                        $(
                            '<li ' + klass + ' data-dtr-index="' + col.columnIndex + '" data-dt-row="' + col.rowIndex + '" data-dt-column="' + col.columnIndex + '">' +
                            '<span class="dtr-title">' +
                            col.title +
                            '</span> ' +
                            '</li>'
                        )
                            .append($('<span class="dtr-data"/>').append(_childNodes(api, col.rowIndex, col.columnIndex)))// api.cell( col.rowIndex, col.columnIndex ).node().childNodes ) )
                            .appendTo(ul);

                        found = true;
                    }
                });

                return found ?
                    ul :
                    false;
            };
        },

        listHidden: function () {
            return function (api, rowIdx, columns) {
                var data = $.map(columns, function (col) {
                    var klass = col.className ?
                        'class="' + col.className + '"' :
                        '';

                    return col.hidden ?
                        '<li ' + klass + ' data-dtr-index="' + col.columnIndex + '" data-dt-row="' + col.rowIndex + '" data-dt-column="' + col.columnIndex + '">' +
                        '<span class="dtr-title">' +
                        col.title +
                        '</span> ' +
                        '<span class="dtr-data">' +
                        col.data +
                        '</span>' +
                        '</li>' :
                        '';
                }).join('');

                return data ?
                    $('<ul data-dtr-index="' + rowIdx + '" class="dtr-details"/>').append(data) :
                    false;
            }
        },

        tableAll: function (options) {
            options = $.extend({
                tableClass: ''
            }, options);

            return function (api, rowIdx, columns) {
                var data = $.map(columns, function (col) {
                    var klass = col.className ?
                        'class="' + col.className + '"' :
                        '';

                    return '<tr ' + klass + ' data-dt-row="' + col.rowIndex + '" data-dt-column="' + col.columnIndex + '">' +
                        '<td>' + col.title + ':' + '</td> ' +
                        '<td>' + col.data + '</td>' +
                        '</tr>';
                }).join('');

                return $('<table class="' + options.tableClass + ' dtr-details" width="100%"/>').append(data);
            }
        }
    };

    /**
     * Responsive default settings for initialisation
     *
     * @namespace
     * @name Responsive.defaults
     * @static
     */
    Responsive.defaults = {
        /**
         * List of breakpoints for the instance. Note that this means that each
         * instance can have its own breakpoints. Additionally, the breakpoints
         * cannot be changed once an instance has been creased.
         *
         * @type {Array}
         * @default Takes the value of `Responsive.breakpoints`
         */
        breakpoints: Responsive.breakpoints,

        /**
         * Enable / disable auto hiding calculations. It can help to increase
         * performance slightly if you disable this option, but all columns would
         * need to have breakpoint classes assigned to them
         *
         * @type {Boolean}
         * @default  `true`
         */
        auto: true,

        /**
         * Details control. If given as a string value, the `type` property of the
         * default object is set to that value, and the defaults used for the rest
         * of the object - this is for ease of implementation.
         *
         * The object consists of the following properties:
         *
         * * `display` - A function that is used to show and hide the hidden details
         * * `renderer` - function that is called for display of the child row data.
         *   The default function will show the data from the hidden columns
         * * `target` - Used as the selector for what objects to attach the child
         *   open / close to
         * * `type` - `false` to disable the details display, `inline` or `column`
         *   for the two control types
         *
         * @type {Object|string}
         */
        details: {
            display: Responsive.display.childRow,

            renderer: Responsive.renderer.listHidden(),

            target: 0,

            type: 'inline'
        },

        /**
         * Orthogonal data request option. This is used to define the data type
         * requested when Responsive gets the data to show in the child row.
         *
         * @type {String}
         */
        orthogonal: 'display'
    };


    /*
     * API
     */
    var Api = $.fn.dataTable.Api;

// Doesn't do anything - work around for a bug in DT... Not documented
    Api.register('responsive()', function () {
        return this;
    });

    Api.register('responsive.index()', function (li) {
        li = $(li);

        return {
            column: li.data('dtr-index'),
            row: li.parent().data('dtr-index')
        };
    });

    Api.register('responsive.rebuild()', function () {
        return this.iterator('table', function (ctx) {
            if (ctx._responsive) {
                ctx._responsive._classLogic();
            }
        });
    });

    Api.register('responsive.recalc()', function () {
        return this.iterator('table', function (ctx) {
            if (ctx._responsive) {
                ctx._responsive._resizeAuto();
                ctx._responsive._resize();
            }
        });
    });

    Api.register('responsive.hasHidden()', function () {
        var ctx = this.context[0];

        return ctx._responsive ?
            $.inArray(false, ctx._responsive._responsiveOnlyHidden()) !== -1 :
            false;
    });

    Api.registerPlural('columns().responsiveHidden()', 'column().responsiveHidden()', function () {
        return this.iterator('column', function (settings, column) {
            return settings._responsive ?
                settings._responsive._responsiveOnlyHidden()[column] :
                false;
        }, 1);
    });


    /**
     * Version information
     *
     * @name Responsive.version
     * @static
     */
    Responsive.version = '2.2.5';


    $.fn.dataTable.Responsive = Responsive;
    $.fn.DataTable.Responsive = Responsive;

// Attach a listener to the document which listens for DataTables initialisation
// events so we can automatically initialise
    $(document).on('preInit.dt.dtr', function (e, settings, json) {
        if (e.namespace !== 'dt') {
            return;
        }

        if ($(settings.nTable).hasClass('responsive') ||
            $(settings.nTable).hasClass('dt-responsive') ||
            settings.oInit.responsive ||
            DataTable.defaults.responsive
        ) {
            var init = settings.oInit.responsive;

            if (init !== false) {
                new Responsive(settings, $.isPlainObject(init) ? init : {});
            }
        }
    });


    return Responsive;
}));
